/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://gitee.com/newgolo/embedme.git
 * Copyright 2014~2020 @ ShenZhen ,China
*******************************************************************************/
#ifdef OS_CYGWIN
#else
#include "BaseType.h"
#include "Tracer.h"
#include "OssAudio.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/soundcard.h>

#define IS_REC_ENABLE(flag)     BIT_GET(flag, 0)
#define IS_PLAY_ENABLE(flag)    BIT_GET(flag, 1)
#define REC_ENABLE(flag)        {flag=BIT_SET(flag, 0);} 
#define REC_DISABLE(flag)       {flag=BIT_CLR(flag, 0);}  
#define PLAY_ENABLE(flag)       {flag=BIT_SET(flag, 1);}   
#define PLAY_DISABLE(flag)      {flag=BIT_CLR(flag, 1);}

namespace libemb{
/**
 *  @class  AudioDevice_S
 *  @brief  音频设备结构体 
 */
typedef struct{
    string m_audio;
    string m_mixer;
    string m_sequencer;
}AudioDevice_S;        

static AudioDevice_S AudioDevice[AUDIO_DEV_NUM]=
{
    {"/dev/dsp","/dev/mixer","/dev/sequencer"},
};

/**
 *  @brief  OssAudio构造函数
 *  @param  none
 *  @return none
 */
OssAudio::OssAudio()
{
    AudioAttr_S defAttr={0};
    for(int i=0; i<AUDIO_DEV_NUM; i++)
    {
        m_audioAttr.push_back(defAttr);
    }
}

OssAudio::~OssAudio()
{
}

/**
 *  @brief  配置音频设备
 *  @param  device   设备号,AUDIO_DEV_E
 *  @param  channels 声道数
 *  @param  rate 采样速度
 *  @param  bits 采样位数
 *  @return 成功返回true,失败返回false
 */
bool OssAudio::config(int& device,int channels,int rate,int bits)
{
    if (device>=AUDIO_DEV_NUM)
    {
        return false;
    }
    m_audioAttr[device].m_chns = channels;
    m_audioAttr[device].m_rate = rate;
    m_audioAttr[device].m_bits = bits;
    return true;
}
/**
 *  @brief  开始录音
 *  @param  device   设备号,AUDIO_DEV_E
 *  @param  fileName 录音保存文件
 *  @return 成功返回true,失败返回false
 */
bool OssAudio::recordStart(int& device,const string& fileName)
{   
    if (device>=AUDIO_DEV_NUM)
    {
        return false;
    }
    
    int ret = open(AudioDevice[device].m_audio.c_str(),O_RDONLY);
    if (ret<0)
    {
        return false;
    }
    m_audioAttr[device].m_recFd = ret;
    
    ret = ioctl(m_audioAttr[device].m_recFd,SOUND_PCM_WRITE_CHANNELS,&(m_audioAttr[device].m_chns));
    if (ret<0)
    {
        TRACE_ERR_CLASS("SOUND_PCM_WRITE_CHANNELS error!\n");
        close(m_audioAttr[device].m_recFd);
        return false;
    }

    ret = ioctl(m_audioAttr[device].m_recFd,SOUND_PCM_WRITE_RATE,&(m_audioAttr[device].m_rate));
    if (ret<0)
    {
        TRACE_ERR_CLASS("SOUND_PCM_WRITE_RATE error!\n");
        close(m_audioAttr[device].m_recFd);
        return false;
    }
    
    ret = ioctl(m_audioAttr[device].m_recFd,SOUND_PCM_WRITE_BITS,&(m_audioAttr[device].m_bits));
    if (ret<0)
    {
        TRACE_ERR_CLASS("SOUND_PCM_WRITE_BITS error!\n");
        close(m_audioAttr[device].m_recFd);
        return false;
    }

    if (IS_REC_ENABLE(m_audioAttr[device].m_flag))
    {
        close(m_audioAttr[device].m_recFd);
        return false;
    }

    m_audioAttr[device].m_recFile = NEW_OBJ File;
    if (!m_audioAttr[device].m_recFile->open(fileName.c_str(),IO_MODE_REWR_ORNEW))
    {
        DEL_OBJ(m_audioAttr[device].m_recFile);
        close(m_audioAttr[device].m_recFd);
        return false;
    }
    REC_ENABLE(m_audioAttr[device].m_flag);
    Thread::createTask(this, SELECTOR_Runnable_taskMain(OssAudio::recordTask), &device);
    return true;
}
/**
 *  @brief  停止录音
 *  @param  device   设备号,AUDIO_DEV_E
 *  @return 成功返回true,失败返回false
 */
bool OssAudio::recordStop(int& device)
{
    if (device>=AUDIO_DEV_NUM)
    {
        return false;
    }
    REC_DISABLE(m_audioAttr[device].m_flag);
    return true;
}
/**
 *  @brief  开始放音
 *  @param  device   设备号,AUDIO_DEV_E
 *  @param  fileName 音频文件
 *  @return 成功返回true,失败返回false
 */
bool OssAudio::playStart(int& device,const string& fileName)
{
    if (device>=AUDIO_DEV_NUM)
    {
        return false;
    }
    
    int ret = open(AudioDevice[device].m_audio.c_str(),O_WRONLY);
    if (ret<0)
    {
        return false;
    }
    m_audioAttr[device].m_playFd = ret;
    
    ret = ioctl(m_audioAttr[device].m_playFd,SOUND_PCM_WRITE_CHANNELS,&(m_audioAttr[device].m_chns));
    if (ret<0)
    {
        TRACE_ERR_CLASS("SOUND_PCM_WRITE_CHANNELS error!\n");
        close(m_audioAttr[device].m_playFd);
        return false;
    }

    ret = ioctl(m_audioAttr[device].m_playFd,SOUND_PCM_WRITE_RATE,&(m_audioAttr[device].m_rate));
    if (ret<0)
    {
        TRACE_ERR_CLASS("SOUND_PCM_WRITE_RATE error!\n");
        close(m_audioAttr[device].m_playFd);
        return false;
    }
    
    ret = ioctl(m_audioAttr[device].m_playFd,SOUND_PCM_WRITE_BITS,&(m_audioAttr[device].m_bits));
    if (ret<0)
    {
        TRACE_ERR_CLASS("SOUND_PCM_WRITE_BITS error!\n");
        close(m_audioAttr[device].m_playFd);
        return false;
    }

    if (IS_PLAY_ENABLE(m_audioAttr[device].m_flag))
    {
        close(m_audioAttr[device].m_playFd);
        return false;
    }
    m_audioAttr[device].m_playFile = NEW_OBJ File;
    if (!m_audioAttr[device].m_playFile->open(fileName.c_str(),IO_MODE_RD_ONLY))
    {
        DEL_OBJ(m_audioAttr[device].m_playFile);
        close(m_audioAttr[device].m_playFd);
        return false;
    }
    
    PLAY_ENABLE(m_audioAttr[device].m_flag);
    Thread::createTask(this, SELECTOR_Runnable_taskMain(OssAudio::playTask), &device);
    return true;
}
/**
 *  @brief  停止放音
 *  @param  device   设备号,AUDIO_DEV_E
 *  @return 成功返回true,失败返回false
 */
bool OssAudio::playStop(int& device)
{
    if (device>=AUDIO_DEV_NUM)
    {
        return false;
    }
    PLAY_DISABLE(m_audioAttr[device].m_flag);
    return true;
}

void OssAudio::recordTask(void* args)
{
    int device = *((int*)args);

    if (device>=AUDIO_DEV_NUM)
    {
        return;
    }

    while(IS_REC_ENABLE(m_audioAttr[device].m_flag))
    {
        char buf[1024]={0};
        int len = read(m_audioAttr[device].m_recFd,buf,sizeof(buf));
        if (len>0)
        {
            m_audioAttr[device].m_recFile->writeData(buf, len);
        }  
    }
    close(m_audioAttr[device].m_recFd);
    REC_DISABLE(m_audioAttr[device].m_flag);
    m_audioAttr[device].m_recFile->close();
    DEL_OBJ(m_audioAttr[device].m_recFile);
}

void OssAudio::playTask(void* args)
{
    int device = *((int*)args);
    if (device>=AUDIO_DEV_NUM)
    {
        return;
    }

    while(IS_PLAY_ENABLE(m_audioAttr[device].m_flag))
    {
        char buf[1024]={0};
        int len = m_audioAttr[device].m_playFile->readData(buf, sizeof(buf)-1);
        if (len>0)
        {
            write(m_audioAttr[device].m_playFd, buf, len);
        }
    }
    close(m_audioAttr[device].m_playFd);
    PLAY_DISABLE(m_audioAttr[device].m_flag);
    m_audioAttr[device].m_playFile->close();
    DEL_OBJ(m_audioAttr[device].m_playFile);
}
}
#endif

